package hn.sigit.view.workflow.transaction;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import javax.faces.event.ActionEvent;
import javax.xml.transform.TransformerException;

import hn.sigit.logic.bpm.TransactionController;
import hn.sigit.model.hnd.ladmshadow.Property;
import hn.sigit.model.hnd.ladmshadow.Parcel;
import hn.sigit.model.hnd.ladmshadow.dao.ParcelDAO;
import hn.sigit.model.hnd.ladmshadow.dao.PropertyDAO;

import hn.sigit.view.ResourceBundleHelper;

import org.geotools.data.DataUtilities;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.gml.producer.FeatureTransformer;
import org.geotools.referencing.CRS;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.remoting.WebRemote;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.WKTWriter;

@Name("dataEntryHelper")
@Scope(value=ScopeType.CONVERSATION)
@AutoCreate
public class DataEntryHelper {
	private TransactionController transactionController;
	
	private DataEntryRRRHelper rrrHelper;
	private DataEntrySplitHelper splitHelper;
	private DataEntryMergeHelper mergeHelper;
	private DataEntryParcelDataHelper parcelDataHelper; 
	private DataEntryUndoHelper undoHelper;
	
	private List<Property> requestedProperties;
	private List<Property> neighborProperties;
	private List<Parcel> requestedParcels;
	private List<Parcel> neighborParcels;
	

	private long presentationId;
	private long selectedLadmBaUnitId;
	private long selectedBaUnitId;
	private long selectedSpatialUnitId;
	private Double envelopeLeft;
	private Double envelopeBottom;
	private Double envelopeRight;
	private Double envelopeTop;
	
	private Parcel selectedParcel;
	private Parcel dummyParcel = Parcel.newDummyParcel();
	
	private List<String> presentationWkts = new ArrayList<String>();
	private ResourceBundleHelper resBundle;

	private long fieldTabCounter = 2108;
	
	
	public DataEntryHelper() {
		transactionController = (TransactionController)Component.getInstance("transactionController");
		presentationId = transactionController.getPresentationId();
		
		rrrHelper = new DataEntryRRRHelper(this);
		splitHelper = new DataEntrySplitHelper(this);
		mergeHelper = new DataEntryMergeHelper(this);
		parcelDataHelper = new DataEntryParcelDataHelper(this);
		undoHelper = new DataEntryUndoHelper(this);
		
		//List<PropertyData> propertyDataList = transactionController.getPropertyDataList();
		//setSelectedLadmBaUnitId(propertyDataList.get(0).getBaUnitID());
		
		if (getRequestedParcels() != null && getRequestedParcels().size() > 0) {
			Parcel reqParcel = getRequestedParcels().get(0);
			setSelectedSpatialUnitId(reqParcel.getSuID());
		}
		
		updatePresentationWkts();
	}
	
	public long getPresentationId() {
		return presentationId;
	}
	
	public TransactionController getTransactionController() {
		return transactionController;
	}
	
	public DataEntryRRRHelper getRrrHelper() {
		return rrrHelper;
	}
	public DataEntrySplitHelper getSplitHelper() {
		return splitHelper;
	}
	public DataEntryMergeHelper getMergeHelper() {
		return mergeHelper;
	}
	public DataEntryParcelDataHelper getParcelDataHelper() {
		return parcelDataHelper;
	}
	public DataEntryUndoHelper getUndoHelper() {
		return undoHelper;
	}
	
	public ResourceBundleHelper getResBundle() {
		if (resBundle == null) {
			resBundle = new ResourceBundleHelper();
		}
		return resBundle;
	}

	private void updatePresentationWkts() {
		presentationWkts.clear();

		List<Property> propertyList = PropertyDAO.loadPropertiesByPresentationId(presentationId);
		WKTWriter wktWriter = new WKTWriter();
		for (Property p : propertyList) {
			MultiPolygon multiPolygon = p.getParcelsAsMultiPolygon();
			if (multiPolygon != null)
				presentationWkts.add(wktWriter.write(multiPolygon));
		}
	}

	@WebRemote
	public String getSplitBorderWKT() {
		return splitHelper.getSplitBorderWKT();
	}
	
	@WebRemote
	public String[] getRequestedPropertiesWKT() {
		String[] retval = new String[getRequestedProperties().size()];

		WKTWriter wktWriter = new WKTWriter();

		int i = 0;
		for (Property prop : getRequestedProperties())
			retval[i++] = wktWriter.write(prop.getParcelsAsMultiPolygon());

		return retval;
	}
	
	@WebRemote
	public String[] getNeighborPropertiesWKT() {
		String[] retval = new String[getNeighborProperties().size()];
		WKTWriter wktWriter = new WKTWriter();

		int i = 0;
		for (Property prop : getNeighborProperties())
			retval[i++] = wktWriter.write(prop.getParcelsAsMultiPolygon());

		return retval;
	}
	
	private String getParcelsGML(List<Parcel> parcelList, String propertyType) {
		if (parcelList == null) return null;

		String retval = "";

		FeatureTransformer transform = new FeatureTransformer();
		transform.setEncoding(Charset.defaultCharset());
		transform.setIndentation(4);
		transform.setGmlPrefixing(true);
		
		try {
			SimpleFeatureType TYPE;
			TYPE = DataUtilities.createType("Polygon", "geom:Polygon,name:String,type:String");
			String prefix = "sigit";//(String) TYPE.getUserData().get("prefix");
			String namespace = "http://localhost";//TYPE.getName().getNamespaceURI();
			transform.getFeatureTypeNamespaces().declareDefaultNamespace(prefix, namespace);
			transform.addSchemaLocation(prefix, namespace);
			
			String srsName = CRS.toSRS(TYPE.getCoordinateReferenceSystem());
			if (srsName != null)
				transform.setSrsName(srsName);
			
			//TODO: convertir a GML todas las spatial units de todas las properties de la lista
			SimpleFeatureCollection collection = FeatureCollections.newCollection("internal");
			for (Parcel parcel : parcelList) {
				if (parcel.isAlive()) {
					SimpleFeature sf;
					sf = SimpleFeatureBuilder.build(TYPE, new Object[] { parcel.getShape(), "name1" }, String.valueOf(parcel.getORMID()));
					sf.setAttribute("name", parcel.getFieldTab());
					sf.setAttribute("type", propertyType);
					collection.add(sf);
				}
			}
			
			ByteArrayOutputStream baos = new ByteArrayOutputStream(16834);
			
			transform.setCollectionBounding(true);
			transform.transform(collection, baos);
			retval = baos.toString();
		}
		catch (SchemaException se) {
			se.printStackTrace();
			return null;
		}
		catch (TransformerException te) {
			te.printStackTrace();
			return null;
		}

		return retval;
	}
	
	@WebRemote
	public String getRequestedParcelsGML() {
		final String type = "requested";
		return getParcelsGML(this.getRequestedParcels(), type);
	}
	
	@WebRemote
	public String getNeighborParcelsGML() {
		final String type = "neighbor";
		return getParcelsGML(this.getNeighborParcels(), type);
	}

	private String getPropertiesGML(List<Property> propertyList, String propertyType) {
		if (propertyList == null) return null;

		String retval = "";

		FeatureTransformer transform = new FeatureTransformer();
		transform.setEncoding(Charset.defaultCharset());
		transform.setIndentation(4);
		transform.setGmlPrefixing(true);
		
		try {
			SimpleFeatureType TYPE;
			TYPE = DataUtilities.createType("MultiPolygon", "geom:MultiPolygon,name:String,type:String");
			String prefix = "sigit";//(String) TYPE.getUserData().get("prefix");
			String namespace = "http://localhost";//TYPE.getName().getNamespaceURI();
			transform.getFeatureTypeNamespaces().declareDefaultNamespace(prefix, namespace);
			transform.addSchemaLocation(prefix, namespace);
			
			String srsName = CRS.toSRS(TYPE.getCoordinateReferenceSystem());
			if (srsName != null)
				transform.setSrsName(srsName);
			
			//TODO: convertir a GML todas las spatial units de todas las properties de la lista
			SimpleFeatureCollection collection = FeatureCollections.newCollection("internal");
			for (Property prop : propertyList) {
				SimpleFeature sf;
				sf = SimpleFeatureBuilder.build(TYPE, new Object[] { prop.getParcelsAsMultiPolygon(),"name1" }, String.valueOf(prop.getORMID()));
				sf.setAttribute("name", prop.getName());
				sf.setAttribute("type", propertyType);
				collection.add(sf);
				ByteArrayOutputStream baos = new ByteArrayOutputStream(16834);
				
				transform.setCollectionBounding(true);
				transform.transform(collection, baos);
				
				retval = baos.toString();
			}
		}
		catch (SchemaException se) {
			se.printStackTrace();
			return null;
		}
		catch (TransformerException te) {
			te.printStackTrace();
			return null;
		}

		return retval;
	}

	@WebRemote
	public String getRequestedPropertiesGML() {
		final String type = "requested";
		return getPropertiesGML(this.getRequestedProperties(), type);
	}
	
	@WebRemote
	public String getNeighborPropertiesGML() {
		final String type = "neighbor";
		return getPropertiesGML(this.getNeighborProperties(), type);
	}
	
	public String getMapScript() {
		return "var click = new OpenLayers.Control.Click();" +
				"mapEditor.addControl(click);" +
				"click.activate();" +
				"var vector_style = new OpenLayers.Style({'strokeColor': '#ff0000','strokeWidth': 3});" +
				"var vector_style_map = new OpenLayers.StyleMap({'default': vector_style});" +
				"vlayer.styleMap = vector_style_map;" +
				"var select_feature_control = new OpenLayers.Control.SelectFeature(parcelFeaturesLayer,{multiple: false,toggle: true,multipleKey: 'shiftKey'});" +
				"mapEditor.addControl(select_feature_control);" +
				"select_feature_control.id = 'parcelSelectControl';" +
				
				"var parcel_style_select = new OpenLayers.Style({" +
				"	'fillColor': '#3BB9FF'," +
				"	'fillOpacity': .8," +
				"	'graphicName': 'square'," +
				"	'label': 'X'," +
				"	'pointRadius': 16," +
				"	'strokeColor': '#343434'," +
				"	'strokeDashstyle': 'solid'," +
				"	'strokeWidth': 4" +
				"});" +
				"var parcel_style_map = new OpenLayers.StyleMap({'select': parcel_style_select});" +
				"var parcel_symbolizer_lookup = {" +
				"	'requested': {" +
				"		'fillColor': '#669933', 'fillOpacity': .5, 'strokeColor': '#aaee77', 'strokeWidth': 3, 'pointRadius': 8, 'label': '${name}'" +
				"	}," +
				"	'neighbor': {" +
				"		'fillColor': '#FF0000', 'fillOpacity': .5, 'strokeColor': '#aaee77', 'strokeWidth': 3, 'pointRadius': 8, 'label': '${name}'" +
				"	}" +
				"};" +
				"parcel_style_map.addUniqueValueRules('default', 'type', parcel_symbolizer_lookup);" +
				"parcelFeaturesLayer.styleMap = parcel_style_map;" +
				"drawParcels();";
	}


	

	public long getSelectedLadmBaUnitId() {
		return selectedLadmBaUnitId;
	}

	public long getSelectedBaUnitId() {
		return selectedBaUnitId;
	}
	private void setSelectedBaUnitId(long selectedBaUnitId) {
		if (this.selectedBaUnitId == selectedBaUnitId) return;
		
		this.selectedBaUnitId = selectedBaUnitId;
		
		//mark the RRR fields to be updated/refreshed
		if (rrrHelper != null) {
			rrrHelper.setParcelRightsList(null);
			rrrHelper.setRrrSet(null);
			rrrHelper.setProperty(null);
		}
	}
	
	public long getSelectedSpatialUnitId() {
		return selectedSpatialUnitId;
	}
	public void setSelectedSpatialUnitId(long selectedSpatialUnitId) {
		if (this.selectedSpatialUnitId == selectedSpatialUnitId) return;
		
		this.selectedSpatialUnitId = selectedSpatialUnitId;
		
		selectedParcel = ParcelDAO.loadParcelById(selectedSpatialUnitId);
		Property property = selectedParcel.getProperty();
		
		setSelectedBaUnitId(property.getuID());
		selectedLadmBaUnitId = property.getLadmId();
		
		//TODO: Agregar los vecinos de la BAUnit al envelope
		MultiPolygon mp = property.getParcelsAsMultiPolygon();
		if (mp != null) {
			Polygon mpEnvelope = (Polygon) mp.getEnvelope();
			Coordinate[] coords = mpEnvelope.getCoordinates();
			
			envelopeLeft = envelopeBottom = envelopeRight = envelopeTop = null;
			if (coords.length > 1) {
				Double minx = Double.MAX_VALUE;
				Double miny = Double.MAX_VALUE;
				Double maxx = Double.MIN_VALUE;
				Double maxy = Double.MIN_VALUE;
				for (Coordinate c : coords) {
					if (c.x < minx) minx = c.x;
					if (c.x > maxx) maxx = c.x;
					if (c.y < miny) miny = c.y;
					if (c.y > maxy) maxy = c.y;
				}
				envelopeLeft = minx;
				envelopeBottom = miny;
				envelopeRight = maxx;
				envelopeTop = maxy;
			}
		}
	}

	public Double getEnvelopeLeft() {
		return envelopeLeft;
	}

	public Double getEnvelopeBottom() {
		return envelopeBottom;
	}

	public Double getEnvelopeRight() {
		return envelopeRight;
	}

	public Double getEnvelopeTop() {
		return envelopeTop;
	}
	
	public List<Property> getRequestedProperties() {
		if (requestedProperties == null) {
			requestedProperties = PropertyDAO.loadRequestedPropertiesByPresentationId(this.presentationId);
		}
		
		return requestedProperties;
	}
	public void setRequestedProperties(List<Property> requestedProperties) {
		this.requestedProperties = requestedProperties;
	}
	
	public List<Property> getNeighborProperties() {
		if (neighborProperties == null) {
			neighborProperties = PropertyDAO.loadNeighborPropertiesByPresentationId(this.presentationId);
		}
		
		return neighborProperties;
	}
	public void setNeighborProperties(List<Property> neighborProperties) {
		this.neighborProperties = neighborProperties;
	}
	
	public List<Parcel> getRequestedParcels() {
		if (requestedParcels == null) {
			requestedParcels = ParcelDAO.loadRequestedParcelsByPresentationId(this.presentationId);
		}
		
		return requestedParcels;
	}
	public void setRequestedParcels(List<Parcel> requestedParcels) {
		this.requestedParcels = requestedParcels;
	}
	
	public List<Parcel> getNeighborParcels() {
		if (neighborParcels == null) {
			neighborParcels = ParcelDAO.loadNeighborParcelsByPresentationId(this.presentationId);
		}
		
		return neighborParcels;
	}
	public void setNeighborParcels(List<Parcel> neighborParcels) {
		this.neighborParcels = neighborParcels;
	}
	
	public Parcel getSelectedParcel() {
		if (selectedParcel == null)
			return dummyParcel;
		
		return selectedParcel;
	}

	public void onPropertyClick(ActionEvent ae) {
		ParcelDAO.loadParcelById(selectedBaUnitId);
	}

	public long getFieldTabCounter() {
		return fieldTabCounter++;
	}
	
	
}
